﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using System.IO;

using SharePointEnforcer.Utilities;

namespace SharePointEnforcer
{
    class EnforcerRuleFile
    {
        public EnforcerRuleFile(string fileName)
        {
            XmlDocument doc = EnforcerUtils.Xml.LoadXmlDoc(fileName);

            FullName = EnforcerUtils.Files.Find(fileName);
            Name = new FileInfo(FullName).Name;

            Handlers = new EnforcerHandlerDefCollection(doc.SelectSingleNode("/Rules/Handlers"));

            Actions = new EnforcerActionDefCollection(doc.SelectSingleNode("/Rules/Actions"), Handlers);


        }
        
        public string Name { get; protected set; }
        public string FullName { get; protected set; }

        public EnforcerActionDefCollection Actions { get; protected set; }
        public EnforcerHandlerDefCollection Handlers { get; protected set; }
    }


    class EnforcerActionDefCollection : List<EnforcerActionDef>
    {
        public EnforcerActionDefCollection(XmlNode actionsNode, EnforcerHandlerDefCollection handlers)
        {
            if (actionsNode == null) throw new EnforcerException(Resources.Enforcer.err_ActionsNodeMissing);

            XmlNodeList actions = actionsNode.SelectNodes("Handler");
            if (actions == null || actions.Count < 1) throw new EnforcerException(Resources.Enforcer.err_ActionsNotDefined);

            foreach (XmlNode action in actions)
            {
                EnforcerActionDef newAction= new EnforcerActionDef(action);

                // is the handler defined?  If not, kick it out
                if (!handlers.ContainsKey(newAction.HandlerId)) throw new EnforcerException(Resources.Enforcer.err_ActionHandlerNotDefined, newAction.Id, newAction.HandlerId);

                this.Add(new EnforcerActionDef(action));
            }
        }
    }


    /// <summary>
    /// Represents an individual rule as defined in a rules file
    /// </summary>
    class EnforcerActionDef
    {
        public EnforcerActionDef(XmlNode ruleNode)
        {
            Parameters = new Dictionary<string, string>();

            // ~~ this logic needs to be tested, particularly the xpaths
            if (ruleNode != null)
            {
                EvaluationMessages = new EnforcerRuleMessages(ruleNode.SelectSingleNode("Messages/Evaluation"));
                RepairMessages = new EnforcerRuleMessages(ruleNode.SelectSingleNode("Messages/Evaluation"));

                Description = EnforcerUtils.Xml.GetElementValue(ruleNode.SelectSingleNode("Description"), true);

                XmlNodeList ruleParams = ruleNode.SelectNodes("Parameters/Parameter");
                foreach (XmlNode ruleParam in ruleParams)
                {
                    string key = EnforcerUtils.Xml.GetAttribute(ruleParam, "key");
                    string value = EnforcerUtils.Xml.GetAttribute(ruleParam, "value");

                    if (!string.IsNullOrEmpty(key)) Parameters[key] = value;
                }

                // ~~ load inclusion/exclusion filters
            }
        }

        public string Name { get; protected set; }
        public string FullName { get; protected set; }
        public string Description { get; protected set; }
        public string HandlerId { get; protected set; }
        public string Id { get; protected set; }
        public bool Enabled { get; protected set; }
        public EnforcerRuleScopes Scopes {get; protected set; }

        public EnforcerRuleMessages EvaluationMessages { get; protected set; }
        public EnforcerRuleMessages RepairMessages { get; protected set; }

        public Dictionary<string, string> Parameters { get; protected set; }
    }


    /// <summary>
    /// I do not like how these scopes are structured.  I need to come up with a different approach
    /// </summary>
    class EnforcerRuleScopes
    {
        public EnforcerRuleScopes(string scopes)
        {
            if (string.IsNullOrEmpty(scopes)) return;

            string[] scopeSettings = scopes.ToLower().Split(",; \t".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

            foreach (string scopeSetting in scopeSettings)
            {
                //" scope="Site,RootWebOnly,NonRootWebs,Web" "
                switch (scopeSetting)
                {
                    case "site":
                        this.Site = true;
                        break;

                    case "rootweb":
                        this.RootWeb = true;
                        break;

                    case "rootwebonly":
                        this.RootWebOnly = true;
                        this.RootWeb = true;
                        this.NonRootWeb = false;
                        this.AllWebs = false;
                        break;

                    case "nonrootweb" :
                    case "nonrootwebs":
                        this.NonRootWeb = true;
                        break;

                    case "nonrootwebonly":
                        this.RootWebOnly = false;
                        this.NonRootWeb = true;
                        this.RootWeb = false;
                        break;

                    case "web" :
                    case "webs":
                        this.AllWebs = true;
                        this.NonRootWeb = true;
                        break;

                    case "list":
                    case "lists":
                        this.List = true;
                        break;

                    default:
                        //unknown scope - throw new EnforcerException();
                        break;
                }

                if (this.RootWebOnly)
                {
                    this.AllWebs = false;
                    this.NonRootWeb = false;
                }
            }
        }

        public bool AllWebs { get; protected set; }
        public bool NonRootWeb { get; protected set; }
        public bool RootWeb { get; protected set; }
        public bool RootWebOnly { get; protected set; }
        public bool List { get; protected set; }
        public bool Site { get; protected set; }
    }


    /// <summary>
    /// Represents a message node in the rule
    /// </summary>
    class EnforcerRuleMessages
    {
        public EnforcerRuleMessages(XmlNode messageNode)
        {
            if (messageNode != null)
            {
                Success = EnforcerUtils.Xml.GetElementValue(messageNode.SelectSingleNode("Success"), true);
                Fail = EnforcerUtils.Xml.GetElementValue(messageNode.SelectSingleNode("Fail"), true);
                Ignore = EnforcerUtils.Xml.GetElementValue(messageNode.SelectSingleNode("Ignore"), true);
            }
        }

        public string Success { get; protected set; }
        public string Fail { get; protected set; }
        public string Ignore { get; protected set; }
    }


    class EnforcerHandlerDefCollection : List<EnforcerHandlerDef>
    {
        public EnforcerHandlerDefCollection(XmlNode handlersNode)
        {
            if (handlersNode != null)
            {
                XmlNodeList handlers = handlersNode.SelectNodes("Handler");

                // Handlers are required - if none are present then we cannot proceed
                if (handlers == null || handlers.Count < 1) throw new EnforcerException(Resources.Enforcer.err_HandlersNotDefined);

                foreach (XmlNode handler in handlers)
                {
                    this.Add(new EnforcerHandlerDef(handler));
                }
            }
        }


        public EnforcerHandlerDef this[string key]
        {
            get
            {
                if (string.IsNullOrEmpty(key)) return null;

                foreach (EnforcerHandlerDef def in this)
                {
                    if (string.Compare(key, def.ID, true) == 0) return def;
                }
                return null;
            }
        }


        public bool ContainsKey(string key)
        {
            EnforcerHandlerDef def = this[key];
            return (def != null);
        }
    }


    /// <summary>
    /// Handler definition as listed in the rules file
    /// </summary>
    class EnforcerHandlerDef
    {
        public EnforcerHandlerDef(XmlNode handlerNode)
        {
            ID = EnforcerUtils.Xml.GetAttribute(handlerNode, "id");
            if (string.IsNullOrEmpty(ID)) throw new EnforcerException(Resources.Enforcer.err_HandlerDefNoID);

            Class = EnforcerUtils.Xml.GetAttribute(handlerNode, "class");
            if (string.IsNullOrEmpty(Class)) throw new EnforcerException(Resources.Enforcer.err_HandlerDefNoClass);
        }

        public string ID { get; protected set; }
        public string Class { get; protected set; }
    }


    enum EnforcerRuleMessageType
    {
        Unknown = 0,
        Success = 1,
        Fail = 2,
        Ignore = 4
    }
    
}
